package com.kuangjia.student_ems.service.impl;

import com.kuangjia.student_ems.dto.SalaryRecordDTO;
import com.kuangjia.student_ems.dto.SalarySchemeDTO;
import com.kuangjia.student_ems.service.SalaryService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.stereotype.Service;
import com.kuangjia.student_ems.mapper.SalarySchemeMapper;
import com.kuangjia.student_ems.entity.SalaryScheme;
import org.springframework.beans.factory.annotation.Autowired;
import com.kuangjia.student_ems.mapper.SalaryRecordMapper;
import com.kuangjia.student_ems.entity.SalaryRecord;
import com.kuangjia.student_ems.entity.User;
import com.kuangjia.student_ems.entity.Department;
import com.kuangjia.student_ems.entity.Position;
import com.kuangjia.student_ems.repository.UserRepository;
import com.kuangjia.student_ems.repository.DepartmentRepository;
import com.kuangjia.student_ems.repository.PositionRepository;
import com.kuangjia.student_ems.repository.AttendanceRepository;
import java.time.LocalDate;
import com.kuangjia.student_ems.entity.Attendance;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.math.BigDecimal;
import java.util.HashMap;

@Service
public class SalaryServiceImpl implements SalaryService {
    @Autowired
    private SalarySchemeMapper salarySchemeMapper;

    @Autowired
    private SalaryRecordMapper salaryRecordMapper;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private DepartmentRepository departmentRepository;

    @Autowired
    private PositionRepository positionRepository;

    @Autowired
    private AttendanceRepository attendanceRepository;

    @Override
    public List<SalarySchemeDTO> getAllSalarySchemes() {
        List<SalaryScheme> list = salarySchemeMapper.selectList(null);
        for (SalaryScheme scheme : list) {
            if (scheme.getPosition() == null && scheme.getPositionId() != null) {
                Position pos = positionRepository.findById(scheme.getPositionId()).orElse(null);
                scheme.setPosition(pos);
            }
        }
        return list.stream().map(SalarySchemeDTO::fromEntity).toList();
    }

    @Override
    public SalarySchemeDTO getSalarySchemeById(Long id) {
        return null;
    }

    @Override
    public SalarySchemeDTO createSalaryScheme(SalarySchemeDTO schemeDTO) {
        SalaryScheme scheme = new SalaryScheme();
        scheme.setName(schemeDTO.getName());
        scheme.setPositionId(schemeDTO.getPositionId());
        scheme.setSalaryType(SalaryScheme.SalaryType.valueOf(schemeDTO.getSalaryType()));
        scheme.setBaseSalary(schemeDTO.getBaseSalary());
        scheme.setPerformanceRate(schemeDTO.getPerformanceRate());
        scheme.setHourlyRate(schemeDTO.getHourlyRate());
        scheme.setCommissionRate(schemeDTO.getCommissionRate());
        scheme.setOvertimeRate(schemeDTO.getOvertimeRate());
        scheme.setStatus(schemeDTO.getStatus() != null ? schemeDTO.getStatus() : true);
        salarySchemeMapper.insert(scheme);
        return SalarySchemeDTO.fromEntity(scheme);
    }

    @Override
    public SalarySchemeDTO updateSalaryScheme(Long id, SalarySchemeDTO schemeDTO) {
        SalaryScheme scheme = salarySchemeMapper.selectById(id);
        if (scheme == null) {
            throw new RuntimeException("未找到对应的薪资方案");
        }
        scheme.setName(schemeDTO.getName());
        scheme.setPositionId(schemeDTO.getPositionId());
        scheme.setSalaryType(SalaryScheme.SalaryType.valueOf(schemeDTO.getSalaryType()));
        scheme.setBaseSalary(schemeDTO.getBaseSalary());
        scheme.setPerformanceRate(schemeDTO.getPerformanceRate());
        scheme.setHourlyRate(schemeDTO.getHourlyRate());
        scheme.setCommissionRate(schemeDTO.getCommissionRate());
        scheme.setOvertimeRate(schemeDTO.getOvertimeRate());
        scheme.setStatus(schemeDTO.getStatus() != null ? schemeDTO.getStatus() : true);
        salarySchemeMapper.updateById(scheme);
        return SalarySchemeDTO.fromEntity(scheme);
    }

    @Override
    public void deleteSalaryScheme(Long id) {
        // 判断是否有薪资记录引用该方案
        com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<SalaryRecord> queryWrapper = new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<>();
        queryWrapper.eq("scheme_id", id);
        Long count = salaryRecordMapper.selectCount(queryWrapper);
        if (count != null && count > 0) {
            throw new RuntimeException("该薪资方案已被薪资记录引用，无法删除！");
        }
        // 没有引用才允许删除
        salarySchemeMapper.deleteById(id);
    }

    @Override
    public IPage<SalaryRecordDTO> getSalaryRecords(Page<SalaryRecordDTO> page, Integer year, Integer month, String status, Long userId) {
        com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<SalaryRecord> wrapper = new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<>();
        if (year != null) wrapper.eq("year", year);
        if (month != null) wrapper.eq("month", month);
        if (status != null) wrapper.eq("status", status);
        if (userId != null) wrapper.eq("user_id", userId);
        Page<SalaryRecord> entityPage = new Page<>(page.getCurrent(), page.getSize());
        IPage<SalaryRecord> recordPage = salaryRecordMapper.selectPage(entityPage, wrapper);
        for (SalaryRecord record : recordPage.getRecords()) {
            if (record.getUser() == null && record.getUserId() != null) {
                User user = userRepository.findById(record.getUserId()).orElse(null);
                record.setUser(user);
                if (user != null) {
                    if (user.getDepartmentId() != null && user.getDepartment() == null) {
                        Department dept = departmentRepository.findById(user.getDepartmentId()).orElse(null);
                        user.setDepartment(dept);
                    }
                    if (user.getPositionId() != null && user.getPosition() == null) {
                        Position pos = positionRepository.findById(user.getPositionId()).orElse(null);
                        user.setPosition(pos);
                    }
                }
            }
            if (record.getScheme() == null && record.getSchemeId() != null) {
                SalaryScheme scheme = salarySchemeMapper.selectById(record.getSchemeId());
                record.setScheme(scheme);
            }
        }
        return recordPage.convert(SalaryRecordDTO::fromEntity);
    }

    @Override
    public SalaryRecordDTO getSalaryRecordById(Long id, org.springframework.security.core.Authentication authentication) {
        SalaryRecord record = salaryRecordMapper.selectById(id);
        if (record == null) {
            return null;
        }
        if (authentication == null || authentication.getPrincipal() == null) {
            return null;
        }
        Object principal = authentication.getPrincipal();
        if (!(principal instanceof User user)) {
            return null;
        }
        // HR/部门经理有全部权限
        if (user.getRole() == User.UserRole.HR_ADMIN || user.getRole() == User.UserRole.DEPARTMENT_MANAGER) {
            return SalaryRecordDTO.fromEntity(record);
        }
        // 普通用户只能访问自己的记录
        if (record.getUserId().equals(user.getId())) {
            return SalaryRecordDTO.fromEntity(record);
        }
        return null;
    }

    @Override
    public Map<String, Object> calculateSalary(Integer year, Integer month) {
        return Collections.emptyMap();
    }

    @Override
    public SalaryRecordDTO calculateUserSalary(Long userId, Integer year, Integer month) {
        return null;
    }

    @Override
    public SalaryRecordDTO confirmSalary(Long id) {
        return null;
    }

    @Override
    public SalaryRecordDTO paySalary(Long id) {
        return null;
    }

    @Override
    public Map<String, Object> batchConfirmSalary(Integer year, Integer month) {
        return Collections.emptyMap();
    }

    @Override
    public Map<String, Object> batchPaySalary(Integer year, Integer month) {
        com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<SalaryRecord> wrapper = new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<>();
        if (year != null) wrapper.eq("year", year);
        if (month != null) wrapper.eq("month", month);
        wrapper.eq("status", SalaryRecord.Status.CONFIRMED);
        List<SalaryRecord> records = salaryRecordMapper.selectList(wrapper);
        int paid = 0;
        for (SalaryRecord record : records) {
            record.setStatus(SalaryRecord.Status.PAID);
            salaryRecordMapper.updateById(record);
            paid++;
        }
        Map<String, Object> result = new HashMap<>();
        result.put("paid", paid);
        result.put("totalConfirmed", records.size());
        return result;
    }

    @Override
    public Map<String, Object> getSalaryStatistics(Integer year, Integer month) {
        com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<SalaryRecord> wrapper = new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<>();
        if (year != null) wrapper.eq("year", year);
        if (month != null) wrapper.eq("month", month);
        List<SalaryRecord> records = salaryRecordMapper.selectList(wrapper);
        int totalCount = records.size();
        int paidCount = 0;
        int unpaidCount = 0;
        int confirmedCount = 0;
        BigDecimal totalAmount = BigDecimal.ZERO;
        for (SalaryRecord record : records) {
            if (record.getStatus() == SalaryRecord.Status.PAID) {
                paidCount++;
                if (record.getNetSalary() != null) {
                    totalAmount = totalAmount.add(record.getNetSalary());
                }
            } else {
                unpaidCount++;
            }
            if (record.getStatus() == SalaryRecord.Status.CONFIRMED) {
                confirmedCount++;
            }
        }
        Map<String, Object> result = new HashMap<>();
        result.put("totalAmount", totalAmount);
        result.put("totalCount", totalCount);
        result.put("paidCount", paidCount);
        result.put("unpaidCount", unpaidCount);
        result.put("confirmedCount", confirmedCount);
        return result;
    }

    @Override
    public Map<String, Object> batchGenerateSalaryRecords(Integer year, Integer month) {
        // 1. 查询所有在职员工
        List<User> users = userRepository.findByStatus(User.EmployeeStatus.ACTIVE);
        int created = 0, skipped = 0;
        // 获取一个默认薪资方案（取第一个）
        List<SalaryScheme> schemes = salarySchemeMapper.selectList(null);
        Long defaultSchemeId = schemes.isEmpty() ? null : schemes.get(0).getId();
        for (User user : users) {
            // 2. 检查该员工本月是否已有薪资记录，避免重复
            com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<SalaryRecord> wrapper = new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<>();
            wrapper.eq("user_id", user.getId()).eq("year", year).eq("month", month);
            if (salaryRecordMapper.selectCount(wrapper) > 0) {
                skipped++;
                continue;
            }
            // 3. 统计本月考勤天数
            LocalDate start = LocalDate.of(year, month, 1);
            LocalDate end = start.withDayOfMonth(start.lengthOfMonth());
            List<Attendance> attendances = attendanceRepository.findByUserIdAndDateBetween(user.getId(), start, end);
            int workDays = attendances.size();
            // 4. 查找岗位对应的薪资方案（仅唯一启用方案时分配，否则为null）
            Long schemeId = null;
            if (user.getPositionId() != null) {
                com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<SalaryScheme> schemeWrapper = new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<>();
                schemeWrapper.eq("position_id", user.getPositionId()).eq("status", true);
                List<SalaryScheme> posSchemes = salarySchemeMapper.selectList(schemeWrapper);
                if (posSchemes.size() == 1) {
                    schemeId = posSchemes.get(0).getId(); // 仅唯一方案时分配
                }
            }
            // 5. 生成薪资记录
            SalaryRecord record = new SalaryRecord();
            record.setUserId(user.getId());
            record.setYear(year);
            record.setMonth(month);
            record.setWorkDays(workDays);
            record.setStatus(SalaryRecord.Status.PENDING);
            // 优先用薪资方案的baseSalary，没有方案时用岗位baseSalary
            java.math.BigDecimal baseSalary = java.math.BigDecimal.ZERO;
            if (schemeId != null) {
                SalaryScheme scheme = salarySchemeMapper.selectById(schemeId);
                if (scheme != null && scheme.getBaseSalary() != null) {
                    baseSalary = scheme.getBaseSalary();
                }
            } else if (user.getPosition() != null && user.getPosition().getBaseSalary() != null) {
                baseSalary = user.getPosition().getBaseSalary();
            }
            record.setBaseSalary(baseSalary);
            record.setTotalSalary(baseSalary);
            // 强制赋值社保、公积金
            record.setSocialSecurity(new java.math.BigDecimal("500"));
            record.setHousingFund(new java.math.BigDecimal("600"));
            // 个税逻辑：基本工资大于5000才交税
            java.math.BigDecimal tax = java.math.BigDecimal.ZERO;
            if (baseSalary.compareTo(new java.math.BigDecimal("5000")) > 0) {
                tax = baseSalary.multiply(new java.math.BigDecimal("0.01"));
            }
            record.setTax(tax);
            // 处理扣款项为null时按0
            java.math.BigDecimal attendanceDeduction = record.getAttendanceDeduction() != null ? record.getAttendanceDeduction() : java.math.BigDecimal.ZERO;
            java.math.BigDecimal socialSecurity = record.getSocialSecurity() != null ? record.getSocialSecurity() : java.math.BigDecimal.ZERO;
            java.math.BigDecimal housingFund = record.getHousingFund() != null ? record.getHousingFund() : java.math.BigDecimal.ZERO;
            java.math.BigDecimal taxVal = record.getTax() != null ? record.getTax() : java.math.BigDecimal.ZERO;
            // 实发工资 = 基本工资 - 考勤扣款 - 社保 - 公积金 - 个税
            java.math.BigDecimal netSalary = baseSalary.subtract(attendanceDeduction).subtract(socialSecurity).subtract(housingFund).subtract(taxVal);
            record.setNetSalary(netSalary);
            record.setSchemeId(schemeId);
            salaryRecordMapper.insert(record);
            created++;
        }
        Map<String, Object> result = new HashMap<>();
        result.put("created", created);
        result.put("skipped", skipped);
        result.put("total", users.size());
        return result;
    }

    @Override
    public Map<String, Object> batchDeleteSalaryRecords(Integer year, Integer month) {
        com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<SalaryRecord> wrapper = new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<>();
        if (year != null) wrapper.eq("year", year);
        if (month != null) wrapper.eq("month", month);
        int count = salaryRecordMapper.delete(wrapper);
        Map<String, Object> result = new HashMap<>();
        result.put("deleted", count);
        return result;
    }

    @Override
    public void confirmSalaryByUser(Long recordId, Long userId) {
        SalaryRecord record = salaryRecordMapper.selectById(recordId);
        if (record == null || !record.getUserId().equals(userId)) {
            throw new RuntimeException("无权确认该薪资记录");
        }
        record.setStatus(SalaryRecord.Status.CONFIRMED);
        salaryRecordMapper.updateById(record);
    }

    @Override
    public boolean updateSalaryStatus(Long salaryRecordId, String status, Long userId) {
        SalaryRecord record = salaryRecordMapper.selectById(salaryRecordId);
        if (record == null || !record.getUserId().equals(userId)) {
            return false;
        }
        try {
            SalaryRecord.Status newStatus = SalaryRecord.Status.valueOf(status);
            // 只允许员工将状态变为CONFIRMED或PAID
            if (newStatus != SalaryRecord.Status.CONFIRMED && newStatus != SalaryRecord.Status.PAID) {
                return false;
            }
            record.setStatus(newStatus);
            salaryRecordMapper.updateById(record);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
} 